#pragma once

#include "construct.hpp"
#include "reference_count.hpp"
#include "utility.hpp"

namespace zuck
{
	template<typename T>
	class deleter {
	public:
		inline static void set_memory(T const* data)noexcept {
			::operator delete(data, sizeof(T), std::align_val_t{ zuck::new_align_v<T> });
		}
	};

	template<typename T, typename _Del = deleter<T>>
	class shared_ptr {
	private:
		using _Self = shared_ptr<T, _Del>;
	public:
		using value_type       = T;
		using pointer          = T*;
		using const_pointer    = const T*;
		using reference        = T&;
		using const_reference  = const T&;
		using rvalue_reference = T&&;
		using count_type	   = zuck::size_t;
		using counter          = ref_count*;
		using memory_known     = bool;
		using shared_delete    = _Del;
	private:
		pointer __ptr;
		counter __count;
	public:
		explicit shared_ptr()
			: __ptr(null), __count(null) {}

		inline explicit shared_ptr(T* ptr = null)
			:__ptr(ptr), __count(null)
		{
			if (__ptr) {
				__count = new ref_count(1);
			}
		}

		inline shared_ptr(const T& val)
			: __ptr(null), __count(null)
		{
			__ptr   = new T(val);
			__count = new ref_count(1);
		}

		template<typename ...Args>
		inline shared_ptr(Args&&... args)
			: __ptr(null), __count(null)
		{
			__ptr   = new T{};
			__count = new ref_count(1);
			construct(__ptr, zuck::forward<Args>(args)...);
		}

		inline shared_ptr(const _Self& other)
			: __ptr(other.__ptr), __count(other.__count)
		{
			if (other.valid()) {
				++(*__count);
			}
		}

		inline shared_ptr(_Self&& other)noexcept
			: __ptr(other.__ptr), __count(other.__count)
		{
			other.__ptr   = null;
			other.__count = null;
		}

		inline ~shared_ptr()
		{
			if (!__ptr) { 
				return;
			}
			if (--(*__count) == 0) {
				__destroy();
			}
		}

		[[nodiscard]] inline count_type use_count()const noexcept
		{
			return __ptr ? __count : 0U;
		}

		[[nodiscard]] inline bool unique()const noexcept
		{
			return static_cast<bool>(*__count == 1);
		}

		inline void reset(T* ptr = null)
		{
			if (!ptr) {
				__destroy();
				return;
			}
			delete __ptr;
			__ptr = ptr;
		}

		[[nodiscard]] inline T* get()const noexcept
		{
			return __ptr ? __ptr : null;
		}

		[[nodiscard]] inline bool valid()const noexcept
		{
			return static_cast<bool>(!__ptr);
		}

		inline _Self& operator=(const _Self& other)
		{
			if (__ptr) {
				delete __ptr;
				delete __count;
			}
			__ptr   = other.__ptr;
			__count = other.__count;
			return *this;
		}

		inline _Self& operator=(_Self&& other)noexcept
		{
			if (__ptr) {
				delete __ptr;
				delete __count;
			}
			__ptr   = other.__ptr;
			__count = other.__count;
			other.__ptr   = null;
			other.__count = null;
			return *this;
		}

		[[nodiscard]] inline pointer operator->()noexcept
		{
			return (__ptr ? __ptr : null);
		}

		[[nodiscard]] inline pointer operator->()const noexcept
		{
			return (__ptr ? __ptr : null);
		}

		[[nodiscard]] inline reference operator*()noexcept
		{
			return *__ptr;
		}

		[[nodiscard]] inline reference operator*()const noexcept
		{
			return *__ptr;
		}

	private:
		inline void __destroy() {
			delete __ptr;
			delete __count;
			__ptr   = null;
			__count = null;
		}

		void __init_and_construct(const T& val) {

		}

		void __init_and_construct(T&& val) {

		}

		template<typename ...Args>
		void __init_and_construct(Args&&... args) {
			
		}

	};

    template<typename T>
    class weak_ptr {
    private:

    public:
        using value_type = T;

    public:
        weak_ptr(zuck::shared_ptr<T> const& _Ptr) {}

        

    };

	template<typename T, typename deleter>
	shared_ptr(T)->shared_ptr<T, deleter>;

	template<typename T, typename deleter>
	shared_ptr(T*)->shared_ptr<T, deleter>;

	template<typename T, typename deleter>
	shared_ptr(shared_ptr<T, deleter> const&)->shared_ptr<T, deleter>;

	template<typename T, typename deleter>
	shared_ptr(shared_ptr<T, deleter>&&)->shared_ptr<T, deleter>;

}